<?php

namespace CommonBundle\View;

use CommonBundle\Utils\FixJSON;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Exception\ValidatorException;

trait CreateApiViewMixin
{
    private static $TYPE_OBJECT = 'object';
    private static $TYPE_ARRAY = 'array';

    //protected $requiredCreateProperties = [];
    //protected $acceptedCreateProperties = [];

    protected function defaultCreateValues(): array
    {
        /** Default values */
        return [];
    }

    /**
     * @param array $content
     * @param null $entity
     * @return array
     */
    protected function processCreateContent(array $content, $entity = null)
    {
        /** Default values */
        return $content;
    }

    /**
     * @param $content
     * @param $entity
     * @return mixed
     */
    protected function processEntity($content, $entity)
    {
        return $entity;
    }

    protected function afterCreated($entity)
    {
        /** Created entity */
        return $entity;
    }

    /**
     * Api create view
     *
     * @Route("", name="create", methods={"POST"})
     * @SWG\Response(
     *     response=200,
     *     description="Api create view",
     * )
     * @SWG\Parameter(name="@partial", in="query", type="boolean")
     * @SWG\Parameter(name="@transform", in="query", type="string")
     * @SWG\Parameter(
     *     name="body",
     *     in="body",
     *     description="json content",
     *     type="json",
     *     required=false,
     *     @SWG\Schema(type="object")
     * )
     * @param Request $request
     * @return Response
     * @throws \Exception
     */
    public function createAction(Request $request): Response
    {
        $service = $this->get($this->serviceClass);

        // External content
        $contents = json_decode($request->getContent(), true) ? : [];

        // Partial create
        $partial = $request->query->getBoolean('@partial', false);

        // Transformer
        $transformer = $request->query->get('@transform');

        // Check input type
        $inputType = FixJSON::getJSONType($request->getContent());
        $response = [];
        if($inputType === self::$TYPE_OBJECT) {
            // Single input
            $contents = [$contents];
        }
        elseif($inputType === self::$TYPE_ARRAY) {
            // Multiple input
            // Do nothing ...
            $contents = (array) $contents;
        }
        else {
            $contents = [];
        }

        // Cannot use transactions here for system fault.

        // $em = $this->get('doctrine.orm.entity_manager');
        // $em->beginTransaction();

        try {
            foreach ($contents as $item) {
                // Copy item
                $content = $item;

                // Create entity
                $entity = $service->new();

                // properties process.
                if(
                    property_exists($this, 'requiredCreateProperties') ||
                    property_exists($this, 'acceptedCreateProperties')
                ) {
                    $data = [];

                    if(property_exists($this, 'requiredCreateProperties')) {
                        foreach ($this->requiredCreateProperties as $property) {
                            if (!array_key_exists($property, $content))
                                throw new ValidatorException(ucfirst($property) . " cannot be empty.");
                            $data[$property] = $content[$property];
                        }
                    }

                    if(property_exists($this, 'acceptedCreateProperties')) {
                        foreach ($this->acceptedCreateProperties as $property) {
                            if(array_key_exists($property, $content)) {
                                $data[$property] = $content[$property];
                            }
                        }
                    }

                    $content = $data;
                }

                // process content
                $content = array_merge($content, $this->defaultCreateValues());

                if($transformer) {
                    $data = json_decode($transformer, true);
                    $content = $this->transformContent($content, $data, $entity);
                }
                $content = $this->processCreateContent($content, $entity);

                // process entity
                $entity = $this->processEntity($content, $entity);

                if ($entity = $service->update($entity, $content)) {
                    if($inputType === self::$TYPE_OBJECT) {
                        $response = $this->afterCreated($entity);
                    }
                    elseif($inputType === self::$TYPE_ARRAY) {
                        $response[] = $this->afterCreated($entity);
                    }
                    else {
                        throw new ValidatorException();
                    }
                }
            }

            // $em->commit();
        }
        catch (\Exception $exception) {
            if(!$partial) {
                // $em->rollback();
                throw $exception;
            }
        }

        return $this->Success($response);
    }
}
